def say_hi(text):
    return text.upper() + '!'


# inner func
def speak(text):
    def whisper(t):
        return t.lower() + '...'
    return whisper(text)


def greet(func):
    greeting = func('hi, I am a Python program')
    print(greeting)


# func can return behaviors
def get_speak_func(text, options):
    def down_str():
        return text.lower() + '...'

    def up_str():
        return text.upper() + '...'

    if options == 'u':
        return up_str
    else:
        return down_str


# Objects can behave like func
class Adder:
    def __init__(self, n):
        self.n = n

    def __call__(self, x):
        return self.n + x


if __name__ == '__main__':
    print(say_hi('hi'))
    # func are objects
    say_hello = say_hi
    # can delete original fun name by 'del say_hi'
    print(say_hello('hello'))
    print(say_hello.__name__)
    print(list(map(say_hello, ['wdh', 'zlj', 'ghp', 'yk'])))
    # func can be stored in data structures(str.capitalize-->首字母大写)
    funcs = [say_hello, str.lower, str.capitalize]
    for f in funcs:
        print(f, f('hey, there'))
    greet(funcs[0])     # say_hello
    greet(funcs[1])     # str.lower
    greet(funcs[2])     # str.capitalize
    # func can be nested(内嵌函数，可嵌套)
    print(speak('HELLO, PYTHON TRICKS'))
    # use returned behavior(carry some of the parent function's state(text) with them)
    print(get_speak_func('HELLO, PYTHON TRICKS', 'D')())
    # Objects can behave like func
    plus_3 = Adder(3)
    print(callable(plus_3))
    print(plus_3(4))
